/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
//
// Created by furture on 2018/5/15.
//

#include <list>
#include "core/render/node/render_object.h"
#include "core/render/page/render_page.h"
#include "core/render/node/factory/render_creator.h"
#include "dom_wson.h"
#include "wson/wson.h"
#include "wson/wson_parser.h"
//#ifdef test_log
#include "core/config/core_environment.h"
//#else
//#define LOGW
//#define LOGE
//#endif

namespace WeexCore {


    inline bool keys_order_as_expect(RenderObject *render, bool keyOrderRight) {
        return (render != nullptr && keyOrderRight);
    }


    /**
     * parser wson to render object
     * */
    RenderObject *parserWson2RenderObject(wson_parser &parser, RenderObject *parent, int index, const std::string &pageId, bool reserveStyles) {
        int objectType = parser.nextType();
        if (!parser.isMap(objectType)) {
            parser.skipValue(objectType);
            return nullptr;
        }
        /**
         * because strem key order specified, so will cann't dependecy it's keys order,
         * if key orders right parse one time, if not first parse ref type create render object
         * then parse others attrs
         * */
        int size = parser.nextMapSize();
        std::string ref;
        std::string renderType;
        RenderObject *render = nullptr;
        int state = parser.getState();
        bool keyOrderRight = true;
        for (int i = 0; i < size; i++) {
            std::string objectKey = parser.nextMapKeyUTF8();
            if (0 == strcmp(objectKey.c_str(), "ref")) {
                ref = parser.nextStringUTF8(parser.nextType());
                if (render != nullptr) {
                    // ref may be after type, so need set to render
                    render->set_ref(ref);
                }
            } else if (0 == strcmp(objectKey.c_str(), "type")) {
                renderType = parser.nextStringUTF8(parser.nextType());
                render = (RenderObject *) RenderCreator::GetInstance()->CreateRender(renderType, ref);
                render->set_page_id(pageId);
                if (parent != nullptr) {
                    parent->AddRenderObject(index, render);
                }
            } else if (0 == strcmp(objectKey.c_str(), "attr")) { //attr is map object
                uint8_t attrType = parser.nextType();
                if (parser.isMap(attrType) && keys_order_as_expect(render, keyOrderRight)) {
                    int attrMapSize = parser.nextMapSize();
                    for (int attrIndex = 0; attrIndex < attrMapSize; attrIndex++) {
                        std::string attrKeyString = parser.nextMapKeyUTF8();
                        std::string attrValueString = parser.nextStringUTF8(parser.nextType());
                        render->AddAttr(attrKeyString, attrValueString);
                    }
                } else {
                    keyOrderRight = keys_order_as_expect(render, keyOrderRight);
                    parser.skipValue(attrType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "style")) { //style is map object
                uint8_t styleType = parser.nextType();
                if (parser.isMap(styleType) && keys_order_as_expect(render, keyOrderRight)) {
                    int styleMapSize = parser.nextMapSize();
                    for (int styleIndex = 0; styleIndex < styleMapSize; styleIndex++) {
                        std::string styleKeyString = parser.nextMapKeyUTF8();
                        std::string styleValueString = parser.nextStringUTF8(parser.nextType());
                        render->AddStyle(styleKeyString, styleValueString, reserveStyles);
                    }
                } else {
                    keyOrderRight = keys_order_as_expect(render, keyOrderRight);
                    parser.skipValue(styleType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "event")) {//event is array
                uint8_t eventType = parser.nextType();
                if (parser.isArray(eventType) && keys_order_as_expect(render, keyOrderRight)) {
                    int eventSize = parser.nextArraySize();
                    for (int eventIndex = 0; eventIndex < eventSize; eventIndex++) {
                        std::string eventValue = parser.nextStringUTF8(parser.nextType());
                        if (eventValue.size() > 0) {
                            render->AddEvent(eventValue);
                        }
                    }
                } else {
                    keyOrderRight = keys_order_as_expect(render, keyOrderRight);
                    parser.skipValue(eventType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "children")) {
                uint8_t childType = parser.nextType();
                if (parser.isArray(childType) && keys_order_as_expect(render, keyOrderRight)) {
                    int childSize = parser.nextArraySize();
                    for (int childIndex = 0; childIndex < childSize; childIndex++) {
                        parserWson2RenderObject(parser, render, childIndex, pageId, reserveStyles);
                    }
                } else {
                    keyOrderRight = keys_order_as_expect(render, keyOrderRight);
                    parser.skipValue(childType);
                }
            } else {
                parser.skipValue(parser.nextType());
            }
        }

        /**
         * if key order not right, parse attr&style  events children again
         * */
        if (!keyOrderRight && render != nullptr) {
            parser.restoreToState(state);
            for (int i = 0; i < size; i++) {
                std::string objectKey = parser.nextMapKeyUTF8();
                if (0 == strcmp(objectKey.c_str(), "attr")) { //attr is map object
                    uint8_t attrType = parser.nextType();
                    if (parser.isMap(attrType)) {
                        int attrMapSize = parser.nextMapSize();
                        for (int attrIndex = 0; attrIndex < attrMapSize; attrIndex++) {
                            std::string attrKeyString = parser.nextMapKeyUTF8();
                            std::string attrValueString = parser.nextStringUTF8(parser.nextType());
                            render->AddAttr(attrKeyString, attrValueString);
                        }
                    } else {
                        parser.skipValue(attrType);
                    }
                } else if (0 == strcmp(objectKey.c_str(), "style")) { //style is map object
                    uint8_t styleType = parser.nextType();
                    if (parser.isMap(styleType)) {
                        int styleMapSize = parser.nextMapSize();
                        for (int styleIndex = 0; styleIndex < styleMapSize; styleIndex++) {
                            std::string styleKeyString = parser.nextMapKeyUTF8();
                            std::string styleValueString = parser.nextStringUTF8(parser.nextType());
                            render->AddStyle(styleKeyString, styleValueString, reserveStyles);
                        }
                    } else {
                        parser.skipValue(styleType);
                    }
                } else if (0 == strcmp(objectKey.c_str(), "event")) {//event is array
                    uint8_t eventType = parser.nextType();
                    if (parser.isArray(eventType)) {
                        int eventSize = parser.nextArraySize();
                        for (int eventIndex = 0; eventIndex < eventSize; eventIndex++) {
                            std::string eventValue = parser.nextStringUTF8(parser.nextType());
                            if (eventValue.size() > 0) {
                                render->AddEvent(eventValue);
                            }
                        }
                    } else { ;
                        parser.skipValue(eventType);
                    }
                } else if (0 == strcmp(objectKey.c_str(), "children")) {
                    uint8_t childType = parser.nextType();
                    if (parser.isArray(childType)) {
                        int childSize = parser.nextArraySize();
                        for (int childIndex = 0; childIndex < childSize; childIndex++) {
                            parserWson2RenderObject(parser, render, childIndex, pageId, reserveStyles);
                        }
                    } else {
                        parser.skipValue(childType);
                    }
                } else {
                    parser.skipValue(parser.nextType());
                }
            }
        }


        if (render != nullptr) {
            render->ApplyDefaultStyle(reserveStyles);
            render->ApplyDefaultAttr();
        }
        return render;
    }


    RenderObject *parserWson2RenderObjectNew(wson_parser &parser, RenderObject *parent, int index, const std::string &pageId, bool reserveStyles) {
        int objectType = parser.nextType();
        if (!parser.isMap(objectType)) {
            parser.skipValue(objectType);
            return nullptr;
        }

        /**
         * because strem key order specified, so will cann't dependecy it's keys order,
         * if key orders right parse one time, if not first parse ref type create render object
         * then parse others attrs
         * */
        int size = parser.nextMapSize();
        std::string ref;
        std::string renderType;
        std::map<std::string, std::string> attrs;
        std::map<std::string, std::string> styles;
        std::vector<std::string> events;
        std::vector<RenderObject *> childrens;

        RenderObject *render = nullptr;
        for (int i = 0; i < size; i++) {
            std::string objectKey = parser.nextMapKeyUTF8();
            if (0 == strcmp(objectKey.c_str(), "ref")) {
                ref = parser.nextStringUTF8(parser.nextType());
            } else if (0 == strcmp(objectKey.c_str(), "type")) {
                renderType = parser.nextStringUTF8(parser.nextType());
            } else if (0 == strcmp(objectKey.c_str(), "attr")) { //attr is map object
                uint8_t attrType = parser.nextType();
                if (parser.isMap(attrType)) {
                    int attrMapSize = parser.nextMapSize();
                    for (int attrIndex = 0; attrIndex < attrMapSize; attrIndex++) {
                        std::string attrKeyString = parser.nextMapKeyUTF8();
                        std::string attrValueString = parser.nextStringUTF8(parser.nextType());
                        attrs.insert({attrKeyString, attrValueString});
                    }
                } else {
                    parser.skipValue(attrType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "style")) { //style is map object
                uint8_t styleType = parser.nextType();
                if (parser.isMap(styleType)) {
                    int styleMapSize = parser.nextMapSize();
                    for (int styleIndex = 0; styleIndex < styleMapSize; styleIndex++) {
                        std::string styleKeyString = parser.nextMapKeyUTF8();
                        std::string styleValueString = parser.nextStringUTF8(parser.nextType());
                        styles.insert({styleKeyString, styleValueString});
                    }
                } else {
                    parser.skipValue(styleType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "event")) {//event is array
                uint8_t eventType = parser.nextType();
                if (parser.isArray(eventType)) {
                    int eventSize = parser.nextArraySize();
                    for (int eventIndex = 0; eventIndex < eventSize; eventIndex++) {
                        std::string eventValue = parser.nextStringUTF8(parser.nextType());
                        if (eventValue.size() > 0) {
                            events.push_back(eventValue);
                        }
                    }
                } else {
                    parser.skipValue(eventType);
                }
            } else if (0 == strcmp(objectKey.c_str(), "children")) {
                uint8_t childType = parser.nextType();
                if (parser.isArray(childType)) {
                    int childSize = parser.nextArraySize();
                    for (int childIndex = 0; childIndex < childSize; childIndex++) {
                        RenderObject *child = parserWson2RenderObjectNew(parser, nullptr, childIndex, pageId, reserveStyles);
                        if (child != nullptr) {
                            childrens.push_back(child);
                        }
                    }
                } else {
                    parser.skipValue(childType);
                }
            } else {
                parser.skipValue(parser.nextType());
            }
        }

        render = (RenderObject *) RenderCreator::GetInstance()->CreateRender(renderType, ref);
        render->set_page_id(pageId);


        for (auto &it: attrs) {
            render->AddAttr(it.first, it.second);
        }

        for (auto &it: styles) {
            render->AddStyle(it.first, it.second, reserveStyles);
        }

        for (auto &it: events) {
            render->AddEvent(it);
        }

        int childIndex = 0;
        for (auto &child: childrens) {
            render->AddRenderObject(childIndex, child);
            childIndex++;
        }


        if (render != nullptr) {
            render->ApplyDefaultStyle(reserveStyles);
            render->ApplyDefaultAttr();
        }
        return render;
    }


    RenderObject *Wson2RenderObject(const char *data, const std::string &pageId, bool reserveStyles) {
        if (!data) {
            return nullptr;
        }
        wson_parser parser(data);
        RenderObject *res;
        if (WXCoreEnvironment::getInstance()->isUseRunTimeApi()) {
            res = parserWson2RenderObjectNew(parser, nullptr, 0, pageId, reserveStyles);
        } else {
            res = parserWson2RenderObject(parser, nullptr, 0, pageId, reserveStyles);
        }
        return res;
    }

    std::vector<std::pair<std::string, std::string>> *Wson2Pairs(const char *data) {
        if (!data) {
            return nullptr;
        }
        wson_parser parser(data);
        std::vector<std::pair<std::string, std::string>> *pairs = nullptr;
        uint8_t type = parser.nextType();
        if (parser.isMap(type)) {
            pairs = new std::vector<std::pair<std::string, std::string>>();
            int mapSize = parser.nextMapSize();
            for (int index = 0; index < mapSize; index++) {
                std::string mapKeyString = parser.nextMapKeyUTF8();
                std::string mapValueString = parser.nextStringUTF8(parser.nextType());
                std::pair<std::string, std::string> mapPair(mapKeyString, mapValueString);
                pairs->insert(pairs->end(), mapPair);
            }
        }
        return pairs;
    };

    static void WsonGenerate(wson_parser &parser, const std::string &parentRef, int index, const WsonObjectGenerator &genObject) {
        int objectType = parser.nextType();
        if (!parser.isMap(objectType)) {
            parser.skipValue(objectType);
            return;
        }
        /**
         * because strem key order specified, so will cann't dependecy it's keys order,
         * if key orders right parse one time, if not first parse ref type create render object
         * then parse others attrs
         * */
        std::string ref;
        std::string renderType;
        std::map<std::string, std::string> *styles = new std::map<std::string, std::string>();
        std::map<std::string, std::string> *attrs = new std::map<std::string, std::string>();
        std::set<std::string> *events = new std::set<std::string>();

        int size = parser.nextMapSize();
        int state = parser.getState();
        for (int i = 0; i < size; i++) {
            std::string objectKey = parser.nextMapKeyUTF8();
            if (0 == strcmp(objectKey.c_str(), "ref")) {
                ref = parser.nextStringUTF8(parser.nextType());
            } else if (0 == strcmp(objectKey.c_str(), "type")) {
                renderType = parser.nextStringUTF8(parser.nextType());
            } else if (0 == strcmp(objectKey.c_str(), "attr")) { //attr is map object
                uint8_t attrType = parser.nextType();
                if (parser.isMap(attrType)) {
                    int attrMapSize = parser.nextMapSize();
                    for (int attrIndex = 0; attrIndex < attrMapSize; attrIndex++) {
                        std::string attrKeyString = parser.nextMapKeyUTF8();
                        std::string attrValueString = parser.nextStringUTF8(parser.nextType());
                        (*attrs)[attrKeyString] = attrValueString;
                    }
                }
            } else if (0 == strcmp(objectKey.c_str(), "style")) { //style is map object
                uint8_t styleType = parser.nextType();
                if (parser.isMap(styleType)) {
                    int styleMapSize = parser.nextMapSize();
                    for (int styleIndex = 0; styleIndex < styleMapSize; styleIndex++) {
                        std::string styleKeyString = parser.nextMapKeyUTF8();
                        std::string styleValueString = parser.nextStringUTF8(parser.nextType());
                        (*styles)[styleKeyString] = styleValueString;
                    }
                }
            } else if (0 == strcmp(objectKey.c_str(), "event")) {//event is array
                uint8_t eventType = parser.nextType();
                if (parser.isArray(eventType)) {
                    int eventSize = parser.nextArraySize();
                    for (int eventIndex = 0; eventIndex < eventSize; eventIndex++) {
                        std::string eventValue = parser.nextStringUTF8(parser.nextType());
                        if (eventValue.size() > 0) {
                            events->insert(eventValue);
                        }
                    }
                }
            } else {
                // skip children
                parser.skipValue(parser.nextType());
            }
        }

        // gen one object
        if (!renderType.empty() && !ref.empty()) {
            genObject(ref, renderType, parentRef, styles, attrs, events, index);

            // parse children
            parser.restoreToState(state);
            for (int i = 0; i < size; i++) {
                std::string objectKey = parser.nextMapKeyUTF8();
                if (0 == strcmp(objectKey.c_str(), "children")) {
                    uint8_t childType = parser.nextType();
                    if (parser.isArray(childType)) {
                        int childSize = parser.nextArraySize();
                        for (int childIndex = 0; childIndex < childSize; childIndex++) {
                            WsonGenerate(parser, ref, childIndex, genObject);
                        }
                    } else {
                        break;
                    }
                } else {
                    parser.skipValue(parser.nextType());
                }
            }
        } else {
            delete styles;
            delete attrs;
            delete events;
        }
    }

    void WsonGenerate(const char *data, const std::string &parentRef, int index, const WsonObjectGenerator &genObject) {
        if (!data) {
            return;
        }
        wson_parser parser(data);
        WsonGenerate(parser, parentRef, index, genObject);
    }

}
